package com.shockweb.common.virtualconn;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.Enumeration;
import java.util.HashMap;

import com.shockweb.common.log.*;
import com.shockweb.common.virtualconn.connection.VirtualConnection;
import com.shockweb.common.virtualconn.connection.VirtualConnectionWapper;
import com.shockweb.common.virtualconn.factory.ConnentionFactory;
import com.shockweb.common.virtualconn.factory.ConnentionFactoryWapper;
/**
 * 虚拟化连接池，支持多个ConnentionFactory负载均衡，提供高可靠和高可用性
 * 仅做一些较小的开发工作量，能管理各种连接，包括数据库、密码机、MQ、webService、socket等
 * @author 彭明华
 * 2017-8-4 创建
 */
public class ConnectionPool<T extends VirtualConnection> implements Runnable {
	/**
	 * 连接池的名称
	 */
	private String name;
	/**
	 * 是否停止
	 */
	private boolean stop = false;
	
	/**
	 * 失效后重试时间
	 */
	private ConnectionPoolConfig config = null;
	
	/**
	 * 锁对象
	 */
	private final Object LOCK = new Object();
	/**
	 * 连接地址
	 */
	private Vector<ConnentionFactoryWapper<T>> connentionFactoryWappers = new Vector<ConnentionFactoryWapper<T>>();
	
	/**
	 * 连接池
	 */
	private Vector<VirtualConnectionWapper<T>> connections = new Vector<VirtualConnectionWapper<T>>();
	
	/**
	 * 构造方法
	 * @param name 连接池的名称
	 * @param connentionFactorys 连接工厂
	 * @throws VirtualConnException 返回虚拟连接异常
	 */
	public ConnectionPool(String name,List<ConnentionFactory<T>> connentionFactorys)throws VirtualConnException{
		this(name,new ConnectionPoolConfig(),connentionFactorys);
	}
	/**
	 * 构造方法
	 * @param name 连接池的名称
	 * @param config 连接池的配置
	 * @param connentionFactorys 连接工厂
	 * @throws VirtualConnException 返回虚拟连接异常
	 */
	public ConnectionPool(String name,ConnectionPoolConfig config,List<ConnentionFactory<T>> connentionFactorys)throws VirtualConnException{
		if(config==null){
			throw new VirtualConnException("config is empty");
		}
		if(connentionFactorys==null || connentionFactorys.isEmpty()){
			throw new VirtualConnException("connentionFactorys is empty");
		}
		for(ConnentionFactory<T> factory:connentionFactorys){
			connentionFactoryWappers.add(new ConnentionFactoryWapper<T>(factory));
		}
		this.config = config;
		this.name = name;
	}
	
	/**
	 * 现有连接数
	 * @return 返回现有连接数
	 */
	public int size(){
		synchronized(LOCK){
			return connections.size();
		}
	}


	
	/**
	 * 守护线程检测连接和连接工厂
	 */
	public void run(){
		long cycle=0;
		while(!stop){
			//测试连接工厂的有效性
			for(ConnentionFactoryWapper<T> wapper :connentionFactoryWappers){
				try{
					wapper.check(config.getTryTime());
				}catch(Exception e){
					LogManager.errorLog(this.getClass(), "ConnentionFactoryWapper.check()失败", e);
				}
			}
			synchronized(LOCK){
				//连接超时强制回收
				for(int i=connections.size()-1;i>=0;i--){
					VirtualConnectionWapper<T> wapper = connections.get(i);
					try{
						wapper.retrieve(config.getRetrieveTime());
					}catch(Exception e){
						LogManager.errorLog(this.getClass(), "VirtualConnectionWapper.retrieve()失败", e);
					}
					if(cycle%config.getCheckCycle()==0){
						//测试空余连接的有效性
						try{
							if(!wapper.isUsed()){
								if(!wapper.check()){
									connections.remove(i);
								}
							}
						}catch(Exception e){
							LogManager.errorLog(this.getClass(), "VirtualConnectionWapper.check()失败", e);
						}
					}
				}
				//补充最小连接
				while(connections.size()<config.getMinConn()){
					try{
						newConnection();
					}catch(Exception e){
						break;
					}
				}
			}
			try {
				Thread.sleep(config.getCheckTime());
			} catch (InterruptedException e) {
			}
			cycle++;
		}
	}

	
	/**
	 * 从连接池获取一个连接
	 * @return 返回一个连接
	 */
    public T getConnection() throws VirtualConnException{
    	synchronized(LOCK){
	        VirtualConnectionWapper<T> connWapper = getConn();
	        if (connWapper != null) {
	        	connWapper.used();
	            return (T)connWapper.getVirtualConnection();
	        }else{
	        	return null;
	        }
	        
	    }
    }

    
	/**
	 * 获取一个连接
	 * @return
	 */
    private VirtualConnectionWapper<T> getConn() throws VirtualConnException{
		VirtualConnectionWapper<T> connWapper = getFreeConnection();
		if(connWapper==null){
	        if (config.getMaxConn() == -1 || connections.size() < config.getMaxConn()) {
	        	connWapper = newConnection();
	        }else{
	        	//等待一定的时间获取连接
	            try {
	                Thread.sleep(config.getTimeout());
	            }catch (Exception e) {}
	            connWapper = getFreeConnection();
	            if(connWapper == null){
	            	throw new VirtualConnException("连接已达到上限'" + config.getMaxConn() + "'");
	            }
	        }
		}
        if (connWapper != null) {
            return connWapper;
        }else{
        	throw new VirtualConnException("获取可用连接失败");
        }
    }
    
    /**
     * 获取可用连接
     * @return
     * @throws VirtualConnException
     */
    private VirtualConnectionWapper<T> getFreeConnection()throws VirtualConnException {
        Enumeration<VirtualConnectionWapper<T>> allConnections = connections.elements();
        while (allConnections.hasMoreElements()) {
        	VirtualConnectionWapper<T> connWapper = (VirtualConnectionWapper<T>) allConnections.nextElement();
        	if(!connWapper.isUsed()){
        		return connWapper;
        	}
        }
        return null;
    }
    
    /**
     * 获取连接工厂wapper
     * @return
     * @throws VirtualConnException
     */
    private ConnentionFactoryWapper<T> getConnentionFactory()throws VirtualConnException {
        
        Map<ConnentionFactoryWapper<T>,Integer> sizes = new HashMap<ConnentionFactoryWapper<T>,Integer>();
        int max = 0;
        for(VirtualConnectionWapper<T> connWapper:connections){
        	ConnentionFactoryWapper<T> factoryWapper = connWapper.getConnentionFactoryWapper();
        	if(sizes.containsKey(factoryWapper)){
        		sizes.put(factoryWapper, sizes.get(factoryWapper)+1);
        	}else{
        		sizes.put(factoryWapper, 1);
        	}
        	if(sizes.get(factoryWapper)>max){
        		max = sizes.get(factoryWapper);
        	}
        }
        if(max>0){
            for(int i=1;i<=max;i++){
            	Enumeration<ConnentionFactoryWapper<T>> allFactoryWappers = connentionFactoryWappers.elements();
                while (allFactoryWappers.hasMoreElements()) {
                	ConnentionFactoryWapper<T> factoryWapper = (ConnentionFactoryWapper<T>) allFactoryWappers.nextElement();
                	if(factoryWapper.isAble() && (!sizes.containsKey(factoryWapper) || sizes.get(factoryWapper)==i)){
                		return factoryWapper;
                    }
                }
            }
        }else{
        	Enumeration<ConnentionFactoryWapper<T>> allFactoryWappers = connentionFactoryWappers.elements();
            while (allFactoryWappers.hasMoreElements()) {
            	ConnentionFactoryWapper<T> factoryWapper = (ConnentionFactoryWapper<T>) allFactoryWappers.nextElement();
            	if(factoryWapper.isAble()){
            		return factoryWapper;
                }
            }
        }

        throw new VirtualConnException("不能获取可用的连接工厂");
    }
    
    /**
     * 创建新连接
     * @return
     */
    private VirtualConnectionWapper<T> newConnection()throws VirtualConnException {
    	ConnentionFactoryWapper<T> factoryWapper = null;
        try {
        	factoryWapper = getConnentionFactory();
        	T conn = factoryWapper.getConnentionFactory().createConnection();
        	if(conn==null){
        		throw new VirtualConnException("Can't create null connection for " + factoryWapper.getConnentionFactory().toString());
        	}
        	VirtualConnectionWapper<T> connWapper = new VirtualConnectionWapper<T>(conn,factoryWapper);
        	connections.add(connWapper);
        	return connWapper;
        }catch(VirtualConnException e){
        	throw e;
        }catch (Exception e) {
        	if(factoryWapper!=null){
            	LogManager.errorLog(this.getClass(),"Can't create a new connection for " + factoryWapper.getConnentionFactory().toString() ,e);
                throw new VirtualConnException("Can't create a new connection for " + factoryWapper.getConnentionFactory().toString(),e);
        	}else{
            	LogManager.errorLog(this.getClass(),"Can't create a new connection",e);
                throw new VirtualConnException("Can't create a new connection",e);
        	}
        }
    }

    /**
     * 释放连接池
     */
    public synchronized void destory() {
    	synchronized(LOCK){
	    	stop = true;
	        Enumeration<VirtualConnectionWapper<T>> allConnWappers = connections.elements();
	        while (allConnWappers.hasMoreElements()) {
	        	VirtualConnectionWapper<T> conn = (VirtualConnectionWapper<T>) allConnWappers.nextElement();
	            try {
	                conn.close();
	            }catch (Exception e) {
	            	LogManager.errorLog(this.getClass(), "Can't close connection for conn " + conn.toString(),e);
	            }
	        }
	        connections.removeAllElements();
    	}
    }
    
    /**
     * 返回当前连接池的信息
     */
    public String toString(){
    	return "name:" + name + ",connentionFactory size:" + connentionFactoryWappers.size() + ",connection size:" + connections.size();
    }
}
